package com.gserver.commons.sort.impl;

import java.util.Arrays;

public class RadixSort {

	/**
	 * 取数x上的第d位数字
	 * 
	 * @param x
	 *            数
	 * @param d
	 *            第几位，从低位到高位
	 * @return
	 */
	public int digit(long x, long d) {

		long pow = 1;
		while (--d > 0) {
			pow *= 10;
		}
		return (int) (x / pow % 10);
	}

	/**
	 * 基数排序实现，以升序排序（下面程序中的位记录器count中，从第0个元素到第9个元素依次用来
	 * 记录当前比较位是0的有多少个..是9的有多少个数，而降序时则从第0个元素到第9个元素依次用来 记录当前比较位是9的有多少个..是0的有多少个数）
	 * 
	 * @param arr
	 *            待排序数组
	 * @param digit
	 *            数组中最大数的位数
	 * @return
	 */
	public long[] radixSortAsc(long[] arr) {
		// 从低位往高位循环
		for (int d = 1; d <= getMax(arr); d++) {
			// 临时数组，用来存放排序过程中的数据
			long[] tmpArray = new long[arr.length];
			// 位记数器，从第0个元素到第9个元素依次用来记录当前比较位是0的有多少个..是9的有多少个数
			int[] count = new int[10];
			// 开始统计0有多少个，并存储在第0位，再统计1有多少个，并存储在第1位..依次统计到9有多少个
			for (int i = 0; i < arr.length; i++) {
				count[digit(arr[i], d)] += 1;
			}
			/*
			 * 比如某次经过上面统计后结果为：[0, 2, 3, 3, 0, 0, 0, 0, 0, 0]则经过下面计算后 结果为： [0, 2,
			 * 5, 8, 8, 8, 8, 8, 8, 8]但实质上只有如下[0, 2, 5, 8, 0, 0, 0, 0, 0, 0]中
			 * 非零数才用到，因为其他位不存在，它们分别表示如下：2表示比较位为1的元素可以存放在索引为1、0的
			 * 位置，5表示比较位为2的元素可以存放在4、3、2三个(5-2=3)位置，8表示比较位为3的元素可以存放在
			 * 7、6、5三个(8-5=3)位置
			 */
			for (int i = 1; i < 10; i++) {
				count[i] += count[i - 1];
			}

			/*
			 * 注，这里只能从数组后往前循环，因为排序时还需保持以前的已排序好的 顺序，不应该打
			 * 乱原来已排好的序，如果从前往后处理，则会把原来在前面会摆到后面去，因为在处理某个
			 * 元素的位置时，位记数器是从大到到小（count[digit(arr[i], d)]--）的方式来处
			 * 理的，即先存放索引大的元素，再存放索引小的元素，所以需从最后一个元素开始处理。
			 * 如有这样的一个序列[212,213,312]，如果按照从第一个元素开始循环的话，经过第一轮
			 * 后（个位）排序后，得到这样一个序列[312,212,213]，第一次好像没什么问题，但问题会
			 * 从第二轮开始出现，第二轮排序后，会得到[213,212,312]，这样个位为3的元素本应该
			 * 放在最后，但经过第二轮后却排在了前面了，所以出现了问题
			 */
			for (int i = arr.length - 1; i >= 0; i--) {// 只能从最后一个元素往前处理
				// for (int i = 0; i < arr.length; i++) {//不能从第一个元素开始循环
				tmpArray[count[digit(arr[i], d)] - 1] = arr[i];
				count[digit(arr[i], d)]--;
			}

			System.arraycopy(tmpArray, 0, arr, 0, tmpArray.length);
		}
		return arr;
	}

	/**
	 * 基数排序实现，以降序排序（下面程序中的位记录器count中，从第0个元素到第9个元素依次用来
	 * 记录当前比较位是0的有多少个..是9的有多少个数，而降序时则从第0个元素到第9个元素依次用来 记录当前比较位是9的有多少个..是0的有多少个数）
	 * 
	 * @param arr
	 *            待排序数组
	 * @return
	 */
	public long[] radixSortDesc(long[] arr) {
		for (int d = 1; d <= getMax(arr); d++) {
			long[] tmpArray = new long[arr.length];
			// 位记数器，从第0个元素到第9个元素依次用来记录当前比较位是9的有多少个..是0的有多少个数
			int[] count = new int[10];
			// 开始统计0有多少个，并存储在第9位，再统计1有多少个，并存储在第8位..依次统计
			// 到9有多少个，并存储在第0位
			for (int i = 0; i < arr.length; i++) {
				count[9 - digit(arr[i], d)] += 1;
			}

			for (int i = 1; i < 10; i++) {
				count[i] += count[i - 1];
			}

			for (int i = arr.length - 1; i >= 0; i--) {
				tmpArray[count[9 - digit(arr[i], d)] - 1] = arr[i];
				count[9 - digit(arr[i], d)]--;
			}

			System.arraycopy(tmpArray, 0, arr, 0, tmpArray.length);
		}
		return arr;
	}

	private int getMax(long[] array) {
		int maxlIndex = 0;
		for (int j = 1; j < array.length; j++) {
			if (array[j] > array[maxlIndex]) {
				maxlIndex = j;
			}
		}
		return String.valueOf(array[maxlIndex]).length();
	}

	public static void main(String[] args) {
		long[] ary = new long[] { 123, 321, 132, 212, 213, 312, 21, 223 };
		RadixSort rs = new RadixSort();
		System.out.println("升 - " + Arrays.toString(rs.radixSortAsc(ary)));

		ary = new long[] { 123, 321, 132, 212, 213, 312, 21, 223 };
		System.out.println("降 - " + Arrays.toString(rs.radixSortDesc(ary)));
	}
}